﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SOFM;

namespace SOFM_Bank {
	public partial class frmMain : Form {

		private SOFM.NeuralNetwork neuralNetwork;

		public frmMain() {
			InitializeComponent();
			lblStatus.Text = "Ready";
		}

		/// <summary>
		/// Fill dữ liệu vào Biểu đồ vector đầu vào của mỗi mẫu,
		/// chỉ chọn vector 0 và 1 làm đại diện
		/// </summary>
		private void ShowInputPatternsOnChart() {
			inputDataChart.Series[0].Clear();
			foreach (List<double> pattern in neuralNetwork.Patterns) {
				inputDataChart.Series[0].Add(pattern[0], pattern[1]);
			}
		}

		/// <summary>
		/// Kết thúc 1 vòng lặp.
		/// Clear dữ liệu biểu đồ và fill dữ liệu mới.
		/// Chỉ dùng 2 vector trọng số 0 và 1 làm đại diện.
		/// Khi tọa độ biểu diễn một vector trọng số sẽ ít biến động dần qua quá trình học,
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void nn_EndEpochEvent(object sender, EndEpochEventArgs e) {
			if (chbVisualization.Checked) {
				outputDataChart.Series[0].Clear();
				for (int i = 0; i < neuralNetwork.OutputLayerDimension; i++)
					for (int j = 0; j < neuralNetwork.OutputLayerDimension; j++) {
						outputDataChart.Series[0].Add(neuralNetwork.OutputLayer[i, j].Weights[0], neuralNetwork.OutputLayer[i, j].Weights[1]);
					}
			}

			Application.DoEvents();
		}

		/// <summary>
		/// Xử lý thanh trạng thái (progessbar)
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void nn_EndIterationEvent(object sender, EventArgs e) {
			if (pbStatus.Value < pbStatus.Maximum)
				pbStatus.Value++;
		}

		/// <summary>
		/// Sự kiện click vào listbox chứa danh sách mầu học.
		///  - Biểu diễn vị trí vector đầu vào của mẫu được chọn.
		///  - Biểu diễn vị trí vector trọng số với winner neuron tương ứng
		///  - Biểu diễn vị trí winner neuron tương ứng
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void lbPatterns_SelectedIndexChanged(object sender, EventArgs e) {
			inputDataChart.Series[1].Clear();
			outputDataChart.Series[1].Clear();
			inputDataChart.Series[1].Add(neuralNetwork.Patterns[lbPatterns.SelectedIndex][0], neuralNetwork.Patterns[lbPatterns.SelectedIndex][1]);

			Neuron Winner = neuralNetwork.FindWinner(neuralNetwork.Patterns[lbPatterns.SelectedIndex]);
			outputDataChart.Series[1].Add(Winner.Weights[0], Winner.Weights[1]);

			sofmVisualizer.LightUpThePixel(Winner.Coordinate.X, Winner.Coordinate.Y);

			// Xác định màu của winner neuron
			var color = sofmVisualizer.Matrix[Winner.Coordinate.X, Winner.Coordinate.Y];

			// Xác định index của màu neuron trong danh sách
			var idx = neuralNetwork.UsedColors.IndexOf(color);

			/// Xác định tên cựm - Legent
			var clus = "";
			foreach (var item in neuralNetwork.ExistentClasses) {
				if (item.Value == idx + 1)
					clus = item.Key;
			}
			MessageBox.Show("Thuộc cựm: " + clus + "\nMàu cựm: " + color.Name, "Thông tin mẫu được chọn");

			//MessageBox.Show(nn.DecideWinner(nn.Patterns[lbPatterns.SelectedIndex]));
		}

		/// <summary>
		/// Add tên các cựm - Legent
		/// </summary>
		private void AddLegend() {
			panelLegend.Controls.Clear();
			Label label = new Label();
			label.Name = "lblLegend";
			label.Top = 5;
			label.Left = 5;
			label.Text = "Legend";
			label.AutoSize = true;
			panelLegend.Controls.Add(label);
			for (int i = 0; i < neuralNetwork.ExistentClasses.Count; i++) {
				Label lbl = new Label();
				lbl.Name = "lbl" + neuralNetwork.ExistentClasses.Keys[i];
				lbl.Text = " - " + neuralNetwork.ExistentClasses.Keys[i];
				lbl.Top = 20 * (i + 1);
				lbl.AutoSize = true;
				lbl.Left = 15 + (int)lbl.Font.Size;
				this.panelLegend.Controls.Add(lbl);

				Panel panel = new Panel();
				panel.Name = "panel" + neuralNetwork.ExistentClasses.Keys[i];
				panel.Top = 20 * (i + 1) + (int)lbl.Font.Size / 2;
				panel.Left = 15;
				panel.Width = (int)lbl.Font.Size;
				panel.Height = (int)lbl.Font.Size;
				panel.BackColor = neuralNetwork.UsedColors[i];
				this.panelLegend.Controls.Add(panel);
			}
		}

		private void lbPatterns_Leave(object sender, EventArgs e) {
			inputDataChart.Series[1].Clear();
			outputDataChart.Series[1].Clear();
		}

		private void chbVisualization_CheckedChanged(object sender, EventArgs e) {
			this.nn_EndEpochEvent(this, new EndEpochEventArgs());
		}

		/// <summary>
		/// Fill mẫu học đầu vào vào listbox
		/// </summary>
		private void AddPatternsToListBox() {
			lbPatterns.Items.Clear();
			string patternString;
			for (int i = 0; i < neuralNetwork.Patterns.Count; i++) {
				patternString = "";
				patternString += neuralNetwork.Classes[i] + " ";
				for (int j = 0; j < neuralNetwork.InputLayerDimension; j++)
					patternString += neuralNetwork.Patterns[i][j].ToString("g2") + " ";
				lbPatterns.Items.Add(patternString);
			}
		}

		/// <summary>
		/// Báo lỗi validate
		/// </summary>
		/// <param name="tb"></param>
		/// <param name="s"></param>
		private void SetError(TextBox tb, string s) {
			errorProvider.SetError(tb, s);

			bool errorOnOtherTextBoxControl = false;
			foreach (Control c in this.gbInputParams.Controls) {
				if (c is TextBox) {
					if (errorProvider.GetError(c).Length != 0)
						errorOnOtherTextBoxControl = true;
				}
			}
			if (!errorOnOtherTextBoxControl)
				btnLoadDataAndCreateNetwork.Enabled = true;
			else
				btnLoadDataAndCreateNetwork.Enabled = false;
		}

		/// <summary>
		/// Validate dữ liệu đầu vào
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void tb_Validating(object sender, CancelEventArgs e) {
			TextBox tb = (TextBox)sender;
			if (tb.Text.Length != 0) {
				bool notValidSymbol = false;
				char[] cArray = tb.Text.ToCharArray();
				foreach (char c in cArray) {
					if (tb.Name == "tbEpsilon") {
						if (!Char.IsDigit(c) && c != '.') notValidSymbol = true;
					}
					else {
						if (!Char.IsDigit(c)) notValidSymbol = true;
					}
				}
				if (!notValidSymbol) {
					switch (tb.Name) {
						case "tbNumberOfCards": {
								if (Convert.ToInt32(tb.Text) <= Properties.Settings.Default.MaximalNumberOfNeurons) SetError(tb, "");
								else SetError(tb, "Số nơ-ron đầu ra tối đa là - " + Properties.Settings.Default.MaximalNumberOfNeurons.ToString());
								break;
							}
						case "tbIterationsNumber": {
								if (Convert.ToInt32(tb.Text) <= Properties.Settings.Default.MaximalNumberOfIterations) SetError(tb, "");
								else SetError(tb, "Số vòng lặp tối đa là - " + Properties.Settings.Default.MaximalNumberOfIterations.ToString());
								break;
							}
						case "tbEpsilon": {
								double epsilon = Convert.ToDouble(tb.Text);
								if (epsilon <= Properties.Settings.Default.MaximalEpsilon && epsilon >= Properties.Settings.Default.MinimalEpsilon) SetError(tb, "");
								else SetError(tb, "Giá trị tốc độ học (Epsilon) nằm trong khoảng [" + Properties.Settings.Default.MinimalEpsilon.ToString() + "; " + Properties.Settings.Default.MaximalEpsilon.ToString() + "]");
								break;
							}
					}
				}
				else {
					SetError(tb, "Đầu vào không hợp lệ, vui lòng làm lại...");
				}
			}

		}

		/// <summary>
		/// Enable, disable control khi quá trình học diễn ra
		/// </summary>
		/// <param name="switcher"></param>
		private void SwitchControls(bool switcher) {
			gbInputParams.Enabled = switcher;
			lbPatterns.Enabled = switcher;
			btnTest.Enabled = switcher;
			txtTest.Enabled = switcher;
		}

		/// <summary>
		/// Nạp dữ liệu đầu vào và khởi tạo đối tượng Mạng nơ-ron
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnLoadDataAndCreateNetwork_Click(object sender, EventArgs e) {
			int NumberOfCards = (int)Math.Sqrt(Int32.Parse(tbNumberOfCards.Text));//Số nơ ron đầu ra
			Functions activationFunction = Functions.Gaus;//Hàm lân cận
			foreach (Control c in this.gbInputParams.Controls) {
				if (c is RadioButton) {
					if (((RadioButton)c).Checked) activationFunction = (Functions)Enum.Parse(typeof(Functions), c.Tag.ToString());
				}
			}

			neuralNetwork = new NeuralNetwork(NumberOfCards, Int32.Parse(tbIterationsNumber.Text), Double.Parse(tbEpsilon.Text), activationFunction);
			neuralNetwork.EndEpochEvent += new EndEpochEventHandler(nn_EndEpochEvent);
			neuralNetwork.EndIterationEvent += new EndIterationEventHandler(nn_EndIterationEvent);
			neuralNetwork.Normalize = this.chbNormalize.Checked;

			OpenFileDialog ofd = new OpenFileDialog();
			if (ofd.ShowDialog() == DialogResult.OK) {
				neuralNetwork.ReadDataFromFile(ofd.FileName);
				sofmVisualizer.Matrix = null;
				panelLegend.Visible = false;
				ShowInputPatternsOnChart();//Hiển thị mạng đầu vào

				AddPatternsToListBox();//Đẩy dữ liệu đầu vào vào Listbox

				SwitchControls(false);

				lblStatus.Text = "Quá trình phân cựm đang diễn ra, vui lòng chờ...";
				pbStatus.Visible = true;
				pbStatus.Minimum = 0;
				pbStatus.Value = 0;
				pbStatus.Maximum = Int32.Parse(tbIterationsNumber.Text);

				neuralNetwork.StartLearning();//Thực hiện học

				sofmVisualizer.Matrix = neuralNetwork.ColorSOFM();
				sofmVisualizer.Invalidate();

				panelLegend.Visible = true;
				AddLegend();
				pbStatus.Visible = false;
				lblStatus.Text = "Ready";
				SwitchControls(true);

			}
		}

		/// <summary>
		/// Sự kiện click vào để Kiểm tra mẫu đơn sau quá trình học
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnTest_Click(object sender, EventArgs e) {
			try {
				/*
				var lst = txtTest.Text.Trim().Split(',');
				var items = new List<double>();
				for (int i = 0; i < nn.InputLayerDimension; i++)
				{
					if (i >= lst.Length)
						items.Add(0);
					else
						items.Add(Convert.ToDouble(lst[i]));
				}

				var Winner = nn.FindWinner(items);

				outputDataChart.Series[1].Add(Winner.Weights[0], Winner.Weights[1]);
				sofmVisualizer.LightUpThePixel(Winner.Coordinate.X, Winner.Coordinate.Y);
				var col = sofmVisualizer.Matrix[Winner.Coordinate.X, Winner.Coordinate.Y];
				var idx = nn.UsedColors.IndexOf(col);

				var clus = "";
				foreach (var item in nn.ExistentClasses)
				{
					if (item.Value == idx + 1)
						clus = item.Key;
				}
				MessageBox.Show(col.Name + " : " + clus);
				*/

				var lst = txtTest.Text.Trim().Split(',');
				var items = new List<double>();
				for (int i = 0; i < neuralNetwork.InputLayerDimension; i++) {
					if (i >= lst.Length)
						items.Add(0);
					else
						items.Add(Convert.ToDouble(lst[i]));
				}

				MessageBox.Show("Thuộc cựm: " + neuralNetwork.DecideWinner(items), "Thông tin mẫu cần kiểm tra");
			}
			catch (Exception ex) {
				MessageBox.Show(ex.Message);
			}
		}
	}
}